/**
  ******************************************************************************
  * @file    mdr32f8_spw.c
  * @author  Milandr Application Team
  * @version V1.0.0
  * @date    18/08/2022
  * @brief   This file contains all the SPW firmware functions.
  ******************************************************************************
  * <br><br>
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, MILANDR SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT
  * OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2026 Milandr</center></h2>
  ******************************************************************************
  * FILE mdr32f8_spw.c
  */
  
/* Includes ------------------------------------------------------------------*/
#include "MDR1986VE8T.h"
#include "mdr32f8_spw.h"
#include "mdr32f8_dma.h"


  /** @addtogroup __MDR32F8_StdPeriph_Driver
  * @{
  */

/** @defgroup SPW SPW
  * @{
  */
  
/** @defgroup SPW_Private_Defines SPW Private Defines
  * @{
  */  
#define IS_SPW_ALL_PERIPH(PERIPH)               ((PERIPH) == MDR_SPW0)

#define IS_SPW_DIVCNT(DIVCNT)                   ((DIVCNT) <= 0xFF)
#define IS_SPW_DIVCNTDEF(DIVCNTDEF)             ((DIVCNTDEF) <= 0xFF)

#define IS_SPW_TIMEIN(TIMEIN)                   ((TIMEIN) <= 0x3F)
#define IS_SPW_TIMEOUT(TIMEOUT)                 ((TIMEOUT) <= 0x3F)

#define IS_SPW_PAUSE_DESCTIME(DESCTIME)         ((DESCTIME) <= 0xFF)
#define IS_SPW_PAUSE_RESTIME(RESTIME)           ((RESTIME) <= 0x7FF)

#define IS_SPW_PHY_TRIMR(TRIMR)                 ((TRIMR) <= 0xFF)

/** @} */ /* End of group SPW_Private_Defines */  

/**
  * @brief  SPW Structures for working with DMA
  */
DMA_ChannelInitTypeDef  DMA_InitStr_SPW_RX;
DMA_CtrlDataInitTypeDef DMA_PriCtrlStr_SPW_RX;
DMA_CtrlDataInitTypeDef DMA_AltCtrlStr_SPW_RX;
  
  /** @defgroup SPW_Private_Functions SPW Private Functions
  * @{
  */
  
/**
  * @brief  Resets the SPW peripheral registers to their default reset values.
  * @param  SPWx: Select the SSP peripheral.
  *         This parameter can be one of the following value: MDR_SPW0.
  * @retval None
  */
  
void SPW_DeInit(MDR_SPW_TypeDef* SPWx)
{
    uint32_t i;

    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));

    SPWx->CONTROL = 0;
    SPWx->STATUS = 0;
    SPWx->DIV = 0;
    SPWx->TIME = 0;
    SPWx->PAUSE = 0;
    SPWx->FIFORX = 0;
    SPWx->FIFOTX = 0;
    SPWx->CNTRX_PACK = 0;
    SPWx->CNTRX0_PACK = 0;
    SPWx->NUM_TXDESC = 0;
    SPWx->NUM_RXDESC = 0;
    SPWx->PHY_CNTR = 0;

    for (i = 0; i < 16; i++) 
    {
        SPWx->RXDESC[i] = SPW_DESC_RDY;
        SPWx->TXDESC[i] = 0;
    }
    
}

/**
  * @brief  Initializes the SPW peripheral Clock according to the
  *         specified parameters.
  * @param  SWP_CLK_DIV: specifies the HCLK division factor.
  *         This parameter can be one of the following values:
  *           @arg SPW_CLKdiv1
  *           @arg SPW_CLKdiv2
  *           @arg SPW_CLKdiv4
  *           @arg SPW_CLKdiv8
  *           @arg SPW_CLKdiv16
  *           @arg SPW_CLKdiv32
  *           @arg SPW_CLKdiv64
  *           @arg SPW_CLKdiv128
  * @retval None
  */
void SPW_CLK_en(uint32_t SPW_CLK_DIV)
{
  uint32_t tmpreg;
  
    /* Check the parameters */
   assert_param(IS_SPW_CLOCK_BRG(SPW_CLK_DIV));  

  tmpreg = CLK_CNTR->SPW0_CLK;

  tmpreg |=  SPW_CLK_EN;
  tmpreg &= ~SPW_DIV_Msk;
  tmpreg |=  SPW_CLK_DIV;

  CLK_CNTR->SPW0_CLK = tmpreg;
}

/**
  * @brief  Fills each SPW_InitStruct member with its default value.
  * @param  SPW_InitStruct: pointer to a @ref SPW_InitStruct structure which will be initialized.
  * @retval None
  */
void SPW_StructInit(SPW_InitTypeDef * SPW_InitStruct)
{
    /* General config */
    /* Disable Error_Reset Link interface */
    SPW_InitStruct->SPW_Link_Error_Reset = DISABLE;
    /* Disable Ready_Started transfer Link interface */
    SPW_InitStruct->SPW_Link_Ready_Started = DISABLE;
    /* Disable Autostart transfer Link interface */
    SPW_InitStruct->SPW_Link_Autostart = DISABLE;
    /* Set the divcnt */
    SPW_InitStruct->SPW_DIVCNT = 0;
    /* Set the divcntdef */
    SPW_InitStruct->SPW_DIVCNTDEF = 0;
    /* Set the pause dsctime */
    SPW_InitStruct->SPW_Pause_Dsctime = 0;
    /* Set the pause restime */
    SPW_InitStruct->SPW_Pause_Restime = 0;
    /* Current reduction by 12.5% disable */
    SPW_InitStruct->SPW_PHY_Ir_Down = DISABLE;
    /* Current increase by 12.5% disable */
    SPW_InitStruct->SPW_PHY_Ir_Up = DISABLE;
    /* Enable the transceiver */
    SPW_InitStruct->SPW_PHY_Out_En = DISABLE;
    /* Enable external stable resistor on the SPW_EXTR pin */
    SPW_InitStruct->SPW_PHY_SELR = DISABLE;
    /* 1.2 V reference digital trim */
    SPW_InitStruct->SPW_PHY_TRIM = SPW_TRIM_VALUE_1_2;
    /* Enable REF of SPW */
    SPW_InitStruct->SPW_PHY_EN_BNG = DISABLE;
    /* Trim of the internal current reference resistor */
    SPW_InitStruct->SPW_PHY_TRIMR = 0x80;
    /* Default test phy mode */
    SPW_InitStruct->SPW_PHY_TST = SPW_TST_VALUE_DEFAULT;

}

/**
  * @brief  Initializes the SPW peripheral according to the specified
  *         parameters in the SPW_InitStruct.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the following values:
  *         MDR_SPW0.
  * @param  SPW_InitStruct: pointer to a SPW_InitTypeDef structure
  *         that contains the configuration information for the specified SPW peripheral.
  * @retval None
  */
void SPW_Init(MDR_SPW_TypeDef* SPWx, const SPW_InitTypeDef* SPW_InitStruct)
{
    uint32_t tmpreg_PHY_CNTR;
    uint32_t tmpreg_Control;
    uint32_t tmpreg_DIV;
    //uint32_t tmpreg_Time;
    uint32_t tmpreg_Pause;

    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_Link_Error_Reset));
    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_Link_Ready_Srarted));
    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_Link_Autostart));

    assert_param(IS_SPW_DIVCNT(SPW_InitStruct->SPW_DIVCNT));
    assert_param(IS_SPW_DIVCNTDEF(SPW_InitStruct->SPW_DIVCNTDEF));

    assert_param(IS_SPW_PAUSE_DESCTIME(SPW_InitStruct->SPW_Pause_Dsctime));
    assert_param(IS_SPW_PAUSE_RESTIME(SPW_InitStruct->SPW_Pause_Restime));

    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_PHY_Ir_Down));
    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_PHY_Ir_Up));
    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_PHY_Out_En));
    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_PHY_SELR));
    assert_param(IS_SPW_PHY_TRIM_VALUE(SPW_InitStruct->SPW_PHY_TRIM));
    assert_param(IS_FUNCTIONAL_STATE(SPW_InitStruct->SPW_PHY_EN_BNG));
    assert_param(IS_SPW_PHY_TRIMR(SPW_InitStruct->SPW_PHY_TRIMR));
    assert_param(IS_SPW_PHY_TST_VALUE(SPW_InitStruct->SPW_PHY_TST));

    /* Config the SPW control register */
    tmpreg_Control = (SPW_InitStruct->SPW_Link_Error_Reset << SPW_CNTR_LINKDIS_pos)
                   | (SPW_InitStruct->SPW_Link_Ready_Started << SPW_CNTR_LINKSTART_pos)
                   | (SPW_InitStruct->SPW_Link_Autostart << SPW_CNTR_AUTOSTART_pos);

    /* Write to SPW control register */
    SPWx->CONTROL  = tmpreg_Control;

    /* Config the SPW DIV register */
    tmpreg_DIV = (SPW_InitStruct->SPW_DIVCNT << SPW_DIV_DIVCNT_pos)
                   | (SPW_InitStruct->SPW_DIVCNTDEF << SPW_DIV_DIVCNTDEF_pos);

    /* Write to SPW DIV register */
    SPWx->DIV |= tmpreg_DIV;

    /* Config the SPW Pause register */
    tmpreg_Pause = (SPW_InitStruct->SPW_Pause_Dsctime << SPW_PAUSE_DSCTIME_pos)
                   | (SPW_InitStruct->SPW_Pause_Restime << SPW_PAUSE_RESTIME_pos);

    /* Write to SPW Pause register */
    SPWx->PAUSE |= tmpreg_Pause;

    /* Config the SPW PHY_CNTR register */
    tmpreg_PHY_CNTR = (SPW_InitStruct->SPW_PHY_Ir_Down << SPW_PHY_CNTR_IRDOWN_pos)
                      | (SPW_InitStruct->SPW_PHY_Ir_Up << SPW_PHY_CNTR_IRUP_pos)
                      | (SPW_InitStruct->SPW_PHY_Out_En << SPW_PHY_CNTR_OUT_EN_pos)
                      | (SPW_InitStruct->SPW_PHY_SELR << SPW_PHY_CNTR_SELR_pos)
                      | (SPW_InitStruct->SPW_PHY_TRIM << SPW_PHY_CNTR_TRIM_pos)
                      | (SPW_InitStruct->SPW_PHY_EN_BNG << SPW_PHY_CNTR_EN_BNG_pos)
                      | (SPW_InitStruct->SPW_PHY_TRIMR << SPW_PHY_CNTR_TRIMR_pos)
                      | (SPW_InitStruct->SPW_PHY_TST << SPW_PHY_CNTR_TST_pos);
    
    /* Write to SPW PHY_CNTR register */
    SPWx->PHY_CNTR |= tmpreg_PHY_CNTR;

}

/**
  * @brief  Enables or disables the specified SPW peripheral.
  * @param  SPWx: select the SPW peripheral.
  *         This parameter can be one of the following values:
  *             MDR_SPW0.
  * @param  NewState - @ref FunctionalState - new state of the SPWx peripheral
  * @retval None
  */
void SPW_Cmd(MDR_SPW_TypeDef* SPWx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));
    
    if(NewState == ENABLE)
    {
        SPWx->CONTROL |= SPW_CNTR_SPWEN;
    }
    else
    {
        SPWx->CONTROL &= ~SPW_CNTR_SPWEN;
    }

}
/**
  * @brief  Enables or disables the PHY SpaceWire.
  * @param  SPWx: Select the ETHERNET peripheral.
  *         This parameter can be one of the following values:
  *             MDR_SPW0.
  * @param  NewState - @ref FunctionalState - new state of the PHY.
  * @retval None
  */
void SPW_PHYCmd(MDR_SPW_TypeDef* SPWx, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if(NewState == ENABLE)
    {
        SPWx->PHY_CNTR |= SPW_PHY_CNTR_EN_PHY;
    }
    else
    {
        SPWx->PHY_CNTR &= ~SPW_PHY_CNTR_EN_PHY;
    }

}

/**
  * @brief  Enables or disables the specified SPW interrupts.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the following values:
  *         MDR_SPW0.
  * @param  SPW_IT: specifies the SPW interrupt sources to be enabled or disabled.
  *         This parameter can be any combination of:
  *           @arg SPW_IT_INTENTIME: Interrupt interrupts when receiving a time marker.
  *           @arg SPW_IT_INTENERR: Interrupt when one of the errors occurs: DSCERR, PERR, ESCERR or CREDERR.
  *           @arg SPW_IT_INTENLINK: Interrupt when the connection status changes.
  *           @arg SPW_IT_INTENRXNE: Interrupt if there is at least one 32-bit data word in the receiver FIFO.
  *           @arg SPW_IT_INTERXF: Interrupt when the receiver FIFO is full.
  *           @arg SPW_IT_INTETXE: Interrupt when there is no data in the transmitter FIFO.
  *           @arg SPW_IT_INTETXF: Interrupt when the transmitter FIFO is full).
  *           @arg SPW_IT_INTENRXDES: Interrupt upon completion of receiving a data packet with a sign of the end of the packet.
  *           @arg SPW_IT_INTENTXDES: Interrupt upon completion of the transmission of a data packet with an end-of-packet sign.
  *           @arg SPW_IT_INTERXF34: Interrupt when filling 3/4 FIFO of the receiver.
  * @param  NewState: new state of the specified SPWx interrupts.
  *         This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void SPW_ITConfig (MDR_SPW_TypeDef* SPWx, uint32_t SPW_IT, FunctionalState NewState) 
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_ITS(SPW_IT));
    assert_param(IS_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
    {
        SPWx->CONTROL |= SPW_IT;
    }
    else
    {
        SPWx->CONTROL &= ~SPW_IT;
    }
}

/**
  * @brief  Checks whether the specified SPW flag is set or not.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the following values:
  *             MDR_SPW0.
  * @param  SPW_FLAG: specifies the flag to check.
  *         This parameter can be one of the @ref SPW_Flags_TypeDef values.
  * @retval @ref FlagStatus - The state of SPW flag (SET or RESET).
  */
FlagStatus SPW_GetFlagStatus(MDR_SPW_TypeDef * SPWx, SPW_Flags_TypeDef SPW_Flag)
{
    FlagStatus bitstatus;
    
     /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_FLAG(SPW_Flag));

    if(SPWx->STATUS & SPW_Flag)
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }

    return (bitstatus);
}

/**
  * @brief  Get SPW state Link Interface.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the following values:
  *             MDR_SPW0.
  * @retval @ref SPW_States_TypeDef - The state of SWP Link Interface. 
  *(SPW_State_ErrorReset, SPW_State_ErrorWait, SPW_State_Ready, SPW_State_Started
  * SPW_State_Connecting or SPW_State_Run).
  */
SPW_States_TypeDef SPW_GetLinkState (MDR_SPW_TypeDef * SPWx)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));

    return ((SPW_States_TypeDef) (MDR_SPW0->STATUS & SPW_STATUS_STATE_Msk));
}


/**
  * @brief  Enables or disables the SPW’s DMA interface.
  * @param  SPWx: Select the SPW: peripheral.
  *         This parameter can be one of the following values:
  *         MDR_SPW0.
  * @param  SPW_DMAReq: specifies the DMA request.
  *         This parameter can be any combination of the following values:
  *           @arg SPW_DMATXEN: DMA request on FIFO empty with transmission.
  *           @arg SPW_DMARXEN: DMA request when the receiver FIFO is full.
  * @param  NewState: new state of the DMA Request sources.
  *         This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void SPW_DMACmd(MDR_SPW_TypeDef* SPWx, uint32_t SPW_DMAReq, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_DMAREQ(SPW_DMAReq));
    assert_param(IS_FUNCTIONAL_STATE(NewState));
    
    if (NewState != DISABLE)
    {
        SPWx->CONTROL |= SPW_DMAReq;
    }
    else
    {
        SPWx->CONTROL &= ~SPW_DMAReq;
    }
}

/**
  * @brief  Clears the SPW's pending flags.
  * @param  SPWx: select the SPW peripheral.
  *         This parameter can be one of the MDR_TIMERx values:
            MDR_SPW0
  * @param  Flags: specifies the flag bit mask to clear.
  *         This parameter can be any combination of @ref SPW_Flags_TypeDef values:
  *           @arg SPW_FLAG_PERR
  *           @arg SPW_FLAG_DSCERR
  *           @arg SPW_FLAG_ESCERR
  *           @arg SPW_FLAG_CREDERR
  *           @arg SPW_FLAG_GOTTIME
  *           @arg SPW_FLAG_RXDESC
  *           @arg SPW_FLAG_RXDESC
  * @retval None
  */
void SPW_ClearFlag(MDR_SPW_TypeDef* SPWx, uint32_t Flags)
{
        /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_STATUS(Flags));

    SPWx->STATUS = Flags;  
}

/**
  * @brief  Initializes a transmitter descriptor for sending a packet.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  type_packet: type of the sending packet.
  *         This parameter can be one of the following @ref SPW_Type_Packet values:
  *         SPW_EOP, SPW_EEP.
  * @param  length: the size in byte of the sending packet.
  * @retval None
  */
void SPW_InitTransmitDesc(MDR_SPW_TypeDef* SPWx, SPW_Type_Packet type_packet, uint32_t length)
{
    uint32_t num_txdesc;

        /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_TYPE_PACKET(type_packet));
    
    /* Determining the current transmitter descriptor number */
    num_txdesc = SPWx->NUM_TXDESC;
    
    /* Filling transmitter descriptor */
    SPWx->TXDESC[num_txdesc]  = type_packet;
    SPWx->TXDESC[num_txdesc] |= length;
    
    /* Transmitter descriptor activation */
    SPWx->TXDESC[num_txdesc] |= SPW_DESC_RDY;
}

/**
  * @brief  Returns the most recent received data by the SPW peripheral.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @retval The received data.
  */
uint32_t SPW_ReceiveData(MDR_SPW_TypeDef* SPWx) 
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    
    return SPWx->FIFORX;
}

/**
  * @brief  Transmits single data through the SPWx peripheral.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Data: the data to transmit.
  * @retval None
  */
void SPW_SendData(MDR_SPW_TypeDef* SPWx, uint32_t Data)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));

    /* Transmit Data */
    SPWx->FIFOTX = Data;
}

/**
  * @brief  Send packet by SPWx peripheral.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  type_packet: type of the sending packet.
  *         This parameter can be one of the following @ref SPW_Type_Packet values.     
  * @param  data: Pointer to array to send data.
  * @retval None
  */
void SPW_SendPacket(MDR_SPW_TypeDef* SPWx, SPW_Type_Packet type_packet, uint32_t *data, uint32_t length)
{
    uint32_t i, length_w;
    
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_TYPE_PACKET(type_packet));
    
    SPW_InitTransmitDesc (SPWx, type_packet, length);
    
    // Filling transmitter FIFO buffer
    length_w = length / 4; // Number of written 32-bit words
    if(length % 4)         // If the number of bytes is not a multiple of 4, then increase the number of words by 1
        length_w++;

    for(i = 0; i < length_w; i++)
    { 
        while(!(MDR_SPW0->STATUS & SPW_FLAG_TXE)){} // Wait until FIFOTX is empty (errata 0067)
        SPWx->FIFOTX = data[i]; 
    }
}

/**
  * @brief  SPWx clear RX descriptor.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Num: The number descriptor.
  *         This parameter can be one of the following @ref SPW_Descriptor_Number values.     
  * @retval None
  */
void SPW_ClearRXDesc(MDR_SPW_TypeDef* SPWx, uint32_t Num) 
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_DESC_NUM(Num));
    
    if ( Num != SPW_DESC_ALL)
        SPWx->RXDESC[Num] = SPW_DESC_RDY;
    else
    {   uint32_t i;
        for (i = 0; i < SPW_DESC_ALL; i++)
            SPWx->RXDESC[i] = SPW_DESC_RDY;
    }
}

/**
  * @brief  SPWx clear TX descriptor.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Num: The number descriptor.
  *         This parameter can be one of the following @ref SPW_Descriptor_Number values.     
  * @retval None
  */
void SPW_ClearTXDesc(MDR_SPW_TypeDef* SPWx, uint32_t Num) 
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_DESC_NUM(Num));
    
    if ( Num != SPW_DESC_ALL)
        SPWx->RXDESC[Num] = 0;
    else
    {   
        uint32_t i;
        for (i = 0; i < SPW_DESC_ALL; i++)
            SPWx->RXDESC[i] = 0;
    }
}

/**
  * @brief  SPWx get type of of received packet.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Num: The number descriptor.
  *         This parameter can be one of the following @ref SPW_Descriptor_Number values.     
  * @retval @ref SPW_Type_Packet - The Type of SPW  (SPW_EOP or SPW_EEP).
  */

SPW_Type_Packet SPW_GetRXDescPacketType(MDR_SPW_TypeDef* SPWx, uint32_t Num)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_DESC_NUM(Num));
    
    if ((SPWx->RXDESC[Num] & SPW_DESC_TYPE_Msk) == SPW_EOP)
        return SPW_EOP;
    else
        return SPW_EEP;
}

/**
  * @brief  SPWx get length of of received packet.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Num: The number descriptor.
  *         This parameter can be one of the following @ref SPW_Descriptor_Number values.
  * @retval @ref SPW_Type_Packet - The Type of SPW  (SPW_EOP or SPW_EEP).
  */

uint32_t SPW_GetRXDescPacketLength(MDR_SPW_TypeDef* SPWx, uint32_t Num)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_DESC_NUM(Num));
    
    return (SPWx->RXDESC[Num] & SPW_DESC_LENGHT_Msk);
  
}

/**
  * @brief  SPWx Check for readiness of the receive descriptor.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Num: The number descriptor.
  *         This parameter can be one of the following @ref SPW_Descriptor_Number values.
  * @retval @ref FunctionalState.
  */
FunctionalState SPW_IsRXDescReady (MDR_SPW_TypeDef* SPWx, uint32_t Num)
{
        /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_DESC_NUM(Num));

    if ((SPWx->RXDESC[Num] & SPW_DESC_RDY))
        return ENABLE;
    else
        return DISABLE;
}

/**
  * @brief  SPWx set the TX time marker.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Type: The state of the Broadcast type packet to transmit.
  *         This parameter can be one of the following @ref SPW_Broadcast_Type_Packet values.     
  * @retval None
  */

void SPW_SetTimeTXMarker(MDR_SPW_TypeDef* SPWx, SPW_Broadcast_Type_Packet Type, uint32_t Time)
{
    uint32_t tmpreg_Time;
    
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_SPW_BROADCAST_TYPE(Type));
    assert_param(IS_SPW_TIMEIN(Time));
    
    tmpreg_Time = (Time << SPW_TIME_TIMEIN_pos) | (Type << SPW_TIME_CTRLIN_pos);
    SPWx->TIME |= tmpreg_Time;

}

/**
  * @brief  SPWx get the RX time marker.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  Time: pointer to word to place time marker to.   
  * @retval @ref SPW_Broadcast_Type_Packet - The Type of SPW  (SPW_TIME_CODE or SPW_INTERRUPT_CODE).
  */

SPW_Broadcast_Type_Packet SPW_GetTimeRXMarker(MDR_SPW_TypeDef* SPWx, uint32_t* Time)
{
    
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    
    *Time = ((SPWx->TIME & SPW_TIME_TIMEOUT) >> SPW_TIME_TIMEOUT_pos);
    
    return (SPW_Broadcast_Type_Packet) ((SPWx->TIME & SPW_TIME_CRTLOUT) >> SPW_TIME_CRTLOUT_pos);
}

/**
  * @brief  SPWx  Start time marker transmission.
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @retval None
  */

void SPW_StartTransmissionTimeTXMarker(MDR_SPW_TypeDef* SPWx)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    
    SPWx->TIME |= SPW_TIME_TICKIN;
}

/**
  * @brief  SPWx  Initializing the DMA to receive data. Two DMA structures are used, primary and alternate.
  *         The primary is used to receive size/2 words in the first half of receive_buffer.
  *         The alternate is used to receive size/2 words in the second half of receive_buffer
  * @param  SPWx: Select the SPW peripheral.
  *         This parameter can be one of the values:
  *             MDR_SPW0;
  * @param  receive_buffer: pointer to a buffer to receive data.
  * @param  size: buffer size for receiving data.
  *         This parameter is an even number from 2 to 2048 worlds.
  * @param  dmaRealCh: Real dma channel number to use SPW.
  *         This parameter can be one of the following values:
  *             @arg DMA_RealChannel_0
  *             ......................  
  *             @arg DMA_RealChannel_31
  * @param  useDMAIT: Using dma interrupts to process data.
  *         This parameter can be one of the following @ref FunctionalState.
  * @retval None
  */

void SPW_InitRxDMA(MDR_SPW_TypeDef* SPWx, uint32_t *receive_buffer, uint32_t size, uint8_t dmaRealCh, FunctionalState useDMAIT)
{
    /* Check the parameters */
    assert_param(IS_SPW_ALL_PERIPH(SPWx));
    assert_param(IS_FUNCTIONAL_STATE(useDMAIT));
    assert_param (IS_DMA_CHANNEL(dmaRealCh));
    
    /* DMA Configuration */
    /* Reset all DMA settings */
    DMA_DeInit();
    DMA_StructInit(&DMA_InitStr_SPW_RX);
    
    /* Set Primary Control Data */
    DMA_PriCtrlStr_SPW_RX.DMA_SourceBaseAddr = (uint32_t)(&(SPWx->FIFORX));
    DMA_PriCtrlStr_SPW_RX.DMA_DestBaseAddr = (uint32_t)receive_buffer;
    DMA_PriCtrlStr_SPW_RX.DMA_SourceIncSize = DMA_SourceIncNo;
    DMA_PriCtrlStr_SPW_RX.DMA_DestIncSize = DMA_DestIncWord;
    DMA_PriCtrlStr_SPW_RX.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    DMA_PriCtrlStr_SPW_RX.DMA_Mode = DMA_Mode_PingPong;
    DMA_PriCtrlStr_SPW_RX.DMA_CycleSize = (size/2);
    DMA_PriCtrlStr_SPW_RX.DMA_NumContinuous = DMA_Transfers_1;
    DMA_PriCtrlStr_SPW_RX.DMA_SourceProtCtrl = DMA_SourcePrivileged;
    DMA_PriCtrlStr_SPW_RX.DMA_DestProtCtrl = DMA_DestPrivileged;
    
    /* Set Alternate Control Data */
    DMA_AltCtrlStr_SPW_RX.DMA_SourceBaseAddr = (uint32_t)(&(SPWx->FIFORX));
    DMA_AltCtrlStr_SPW_RX.DMA_DestBaseAddr   = (uint32_t)receive_buffer[size/2];
    DMA_AltCtrlStr_SPW_RX.DMA_SourceIncSize = DMA_SourceIncNo;
    DMA_AltCtrlStr_SPW_RX.DMA_DestIncSize = DMA_DestIncWord;
    DMA_AltCtrlStr_SPW_RX.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    DMA_AltCtrlStr_SPW_RX.DMA_Mode = DMA_Mode_PingPong;
    DMA_AltCtrlStr_SPW_RX.DMA_CycleSize = (size - (size/2));
    DMA_AltCtrlStr_SPW_RX.DMA_NumContinuous = DMA_Transfers_1;
    DMA_AltCtrlStr_SPW_RX.DMA_SourceProtCtrl = DMA_SourcePrivileged;
    DMA_AltCtrlStr_SPW_RX.DMA_DestProtCtrl = DMA_DestPrivileged;
    
    /* Set Channel Structure */
    DMA_InitStr_SPW_RX.DMA_PriCtrlData = &DMA_PriCtrlStr_SPW_RX;
    DMA_InitStr_SPW_RX.DMA_AltCtrlData = &DMA_AltCtrlStr_SPW_RX;
    DMA_InitStr_SPW_RX.DMA_Priority = DMA_Priority_Default;
    DMA_InitStr_SPW_RX.DMA_UseBurst = DMA_BurstClear;
    DMA_InitStr_SPW_RX.DMA_SelectDataStructure = DMA_CTRL_DATA_PRIMARY;

    /* Init DMA channel SPW_RX */
    DMA_Init(dmaRealCh, &DMA_InitStr_SPW_RX);
    
    switch (dmaRealCh)
    {
        case DMA_RealChannel_0: MDR_DMA->CHMUX0 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);   break;
        case DMA_RealChannel_1: MDR_DMA->CHMUX0 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos);  break;
        case DMA_RealChannel_2: MDR_DMA->CHMUX0 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);   break;
        case DMA_RealChannel_3: MDR_DMA->CHMUX0 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos);  break;
        case DMA_RealChannel_4: MDR_DMA->CHMUX1 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);   break;
        case DMA_RealChannel_5: MDR_DMA->CHMUX1 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos);  break;
        case DMA_RealChannel_6: MDR_DMA->CHMUX1 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);   break;
        case DMA_RealChannel_7: MDR_DMA->CHMUX1 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos);  break;
        case DMA_RealChannel_8: MDR_DMA->CHMUX2 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);   break;
        case DMA_RealChannel_9: MDR_DMA->CHMUX2 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos);  break;
        case DMA_RealChannel_10: MDR_DMA->CHMUX2 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);  break;
        case DMA_RealChannel_11: MDR_DMA->CHMUX2 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos); break;
        case DMA_RealChannel_12: MDR_DMA->CHMUX3 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);  break;
        case DMA_RealChannel_13: MDR_DMA->CHMUX3 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos); break;
        case DMA_RealChannel_14: MDR_DMA->CHMUX3 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);  break;
        case DMA_RealChannel_15: MDR_DMA->CHMUX3 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos); break;
        case DMA_RealChannel_16: MDR_DMA->CHMUX4 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);  break;
        case DMA_RealChannel_17: MDR_DMA->CHMUX4 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos); break;
        case DMA_RealChannel_18: MDR_DMA->CHMUX4 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);  break;
        case DMA_RealChannel_19: MDR_DMA->CHMUX4 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos); break;
        case DMA_RealChannel_20: MDR_DMA->CHMUX5 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);  break;
        case DMA_RealChannel_21: MDR_DMA->CHMUX5 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos); break;
        case DMA_RealChannel_22: MDR_DMA->CHMUX5 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);  break;
        case DMA_RealChannel_23: MDR_DMA->CHMUX5 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos); break;
        case DMA_RealChannel_24: MDR_DMA->CHMUX6 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);  break;
        case DMA_RealChannel_25: MDR_DMA->CHMUX6 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos); break;
        case DMA_RealChannel_26: MDR_DMA->CHMUX6 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);  break;
        case DMA_RealChannel_27: MDR_DMA->CHMUX6 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos); break;
        case DMA_RealChannel_28: MDR_DMA->CHMUX7 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FIRST_Pos);  break;
        case DMA_RealChannel_29: MDR_DMA->CHMUX7 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_SECOND_Pos); break;
        case DMA_RealChannel_30: MDR_DMA->CHMUX7 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_THIRD_Pos);  break;
        case DMA_RealChannel_31: MDR_DMA->CHMUX7 |= (DMA_VirtualChannel_SPW_RX << DMA_CHMUX_FOURTH_Pos); break;
    }

    if (useDMAIT)
     {
        /* Enable DMA IRQ */
        NVIC_ClearPendingIRQ(DMA_DONE0_IRQn);
        NVIC_EnableIRQ(DMA_DONE0_IRQn);
     }
}

/** @} */ /* End of group SPW_Private_Functions */

/** @} */ /* End of group SPW */

/** @} */ /* End of group __MDR32F8_StdPeriph_Driver */


/*********************** (C) COPYRIGHT 2026 Milandr ****************************
*
* END OF FILE mdr32f8_spw.c */


